home *** CD-ROM | disk | FTP | other *** search
/ Plug-In Power Pack for Netscape Communicator / Plug-In Power Pack for Netscape Communicator.iso / plugins / dataviews / dvtools / demos / cpitdemo / cpit_dsp.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-05-08  |  18.6 KB  |  670 lines

  1. #ifndef lint
  2. static char SccsId[]= "@(#)cpit_dsp.c    V1.13    3/15/95";
  3. #endif
  4.  
  5. /*------------------------------------------------------------------
  6. | file name -- cpit_dsp.c
  7. |
  8. | functions        Description
  9. | ---------             -----------
  10. | InitDisplays        Initializes the SCREEN and DRAWPORTS
  11. | TermDisplays          Cleans up the SCREEN, DRAWPORTS and DISPLAY_LIST
  12. |
  13. | SwitchDisplay      Display Manager
  14. | OverlayDisplay        Overlay the view.
  15. | RemoveDisplay         Deletes the overlayed view.
  16. |
  17. | preload_views        Preloads all views... (base and overlays)
  18. | create_drawport    Creates the drawport and add is to the table
  19. | get_drawport        Returns the drawport assoiciated with the passed name.
  20. | load_view        Loads the view and adds it to the table.
  21. | get_view        Returns the previously or newly loaded view.
  22. | next_display       Adds a display to the list.
  23. | prev_display          Deletes a display from the list.
  24. | reset_display         Destroys the display list, reset to the top display
  25. | create_display_list   Create a DisplayList for NEXT/PREV.
  26. | del_display_list_item    Delete a display from the DisplayList.
  27. | destroy_display_list  Destroys the DisplayList.
  28. | update_DynObj_list    Creates list for managing dynamic objects.
  29. |
  30. |-----------------------------------------------------------------*/
  31.  
  32. #include "std.h"
  33. #include "dvstd.h"
  34. #include "dvGR.h"
  35. #include "Tfundecl.h"
  36. #include "VTfundecl.h"
  37. #include "VOfundecl.h"
  38. #include "VUerfundecl.h"
  39. #include "VUfundecl.h"
  40. #include "GRfundecl.h"
  41. #include "cpit_vars.h"
  42. #include "cpit_data.h"
  43. #include "cpit_fundecl.h"
  44.  
  45.  
  46.  
  47. #define NEW_LIST        (char)0
  48. #define ADD_TO_LIST        (char)1
  49. #define DELETE_FROM_LIST    (char)2
  50.  
  51. /***************** Begin Function Declarations *************/
  52. LOCAL  void preload_views V_P_((void));
  53. LOCAL  DRAWPORT create_drawport V_P_((OBJECT screen, char *viewname));
  54. LOCAL  DRAWPORT get_drawport V_P_((char *view_name));
  55. LOCAL  VIEW load_view V_P_((char *view_name));
  56. LOCAL  VIEW get_view V_P_((char *view_name));
  57. LOCAL  DRAWPORT next_display V_P_((char *view_name));
  58. LOCAL  DRAWPORT prev_display V_P_((void));
  59. LOCAL  DRAWPORT reset_display V_P_((char *view_name));
  60. LOCAL  DISPLAY_LIST *create_display_list V_P_((void));
  61. LOCAL  void del_display_list_item V_P_((DISPLAY_LIST *display_item));
  62. LOCAL  void destroy_display_list V_P_((DISPLAY_LIST **display_info));
  63. /***************** End Function Declarations *************/
  64.  
  65. /*-----------------------------------------------------------------
  66. |
  67. |  InitDisplays
  68. |       Performs the initialization needed for the display.
  69. |
  70. |       A SCREEN is opened using DVDEVICE and other attributes.
  71. |
  72. |    create_drawport() is called to create a drawport for each
  73. |    display area. create_drawport will be called each time a
  74. |    a new views is needed by the display manager.
  75. */
  76. void
  77. InitDisplays (device)
  78.      char *device;
  79. {
  80.   int error_code;
  81.   char buf[50];
  82.  
  83.   /* Create view and drawport tables needed from display management */
  84.   ViewTable = VTstcreate ("View Names and VIEWs", (VTSTCOMPAREFUNPTR)NULL);
  85.   DpTable = VTstcreate ("View Names and DRAWPORTs", (VTSTCOMPAREFUNPTR)NULL);
  86.  
  87.   /* Open the control window */
  88.   VUoff_copyright ();
  89.   MenuScreen = TscOpenSet (device, (CHAR *) NULL,
  90. #ifdef WINNT
  91.                V_WINDOW_HEIGHT,    70,
  92.                V_WINDOW_WIDTH,     680,
  93.                V_WINDOW_X,         58,
  94.                V_WINDOW_Y,         0,
  95.                V_WIN32_ICON_NAME,        "cpiticon",
  96. #ifdef DOUBLE_BUFFER
  97.                V_WIN32_DOUBLE_BUFFER,  YES,
  98. #endif /* DOUBLE_BUFFER */
  99. #else  /* Not WINNT */
  100.                V_WINDOW_HEIGHT,      70,
  101.                V_WINDOW_Y,           0,
  102.                V_X_DOUBLE_BUFFER,  YES,
  103.                V_X_EXPOSURE_BLOCK, YES,
  104. #endif /* WINNT */
  105.                V_WINDOW_NAME,    "Display Control Panel",
  106.                V_END_OF_LIST );
  107.  
  108. if(!MenuScreen)
  109.  {
  110.    error_code=TscOpenError();
  111. #ifdef WINNT
  112.    sprintf(buf,"Product is not validated. Error code %d.",error_code);
  113.    MessageBox(NULL,buf,"Validation Error",MB_OK);
  114. #else
  115.    fprintf(stderr,"Product is not validated. Error code %d.",error_code);
  116. #endif
  117.    exit(error_code);
  118.   }
  119.  
  120.   /* If we couldn't open a window, exit the program */
  121.   EXIT_IF_INVALID (MenuScreen, NO_DEVICE);
  122.  
  123.   /* Initialize the window events */
  124.   InitSimpleEvents ();
  125.  
  126.   /* Erase the "copyright" message and set the cursor */
  127.   (VOID) TscErase (MenuScreen);
  128.  
  129.   /* Setup the Menu Screen */
  130.   MenuDrawport = create_drawport (MenuScreen, MENU_VIEW);
  131.   (VOID) TdpDraw (MenuDrawport);
  132.  
  133.   /* Open the main window */
  134.   MainScreen = TscOpenSet (device, (CHAR *) NULL,
  135.                            V_WINDOW_NAME, "Aircraft Display",
  136. #ifdef WINNT
  137.                V_WINDOW_HEIGHT,        465,
  138.                V_WINDOW_WIDTH,         680,
  139.                V_WINDOW_X,             58,
  140.                V_WINDOW_Y,             100,
  141.                V_WIN32_ICON_NAME,      "cpiticon",
  142. #ifdef DOUBLE_BUFFER
  143.                V_WIN32_DOUBLE_BUFFER,  YES,
  144. #endif /* DOUBLE_BUFFER */
  145. #else  /* Not WINNT */
  146.                V_WINDOW_Y,           100,
  147.                V_X_DOUBLE_BUFFER,  YES,
  148.                V_X_EXPOSURE_BLOCK, YES,
  149. #endif /* WINNT */
  150.                V_END_OF_LIST );
  151.  
  152.  
  153.   /* If we couldn't open a window, exit the program */
  154.   EXIT_IF_INVALID (MainScreen, NO_DEVICE);
  155.  
  156.   /* Initialize the window events */
  157.   InitSimpleEvents ();
  158.  
  159.   /* Erase the "copyright" message and set the cursor */
  160.   (VOID) TscErase (MainScreen);
  161.  
  162.   /* Create the a wait drawport */
  163.   SwitchDisplay (NEXT_DISPLAY, WAIT_VIEW);
  164.  
  165.   /* Preload the main views */
  166.   preload_views ();
  167.  
  168.   (VOID) GRset (V_ACTIVE_CURSOR, V_END_OF_LIST);
  169.  
  170.   /* Set the Cursor for the menu screen */
  171.   (VOID) TscSetCurrentScreen (MenuScreen);
  172.   (VOID) GRset (V_ACTIVE_CURSOR, V_END_OF_LIST);
  173. }
  174.  
  175. /*-----------------------------------------------------------------
  176. |
  177. |  SwitchDisplay
  178. |       Switches the view being displayed in the MAIN_WINDOW.
  179. |
  180. |    The views are managed in a "DisplayList" which is
  181. |    updated according to the "choice".
  182. |       next_display() - Adds a display to the list.
  183. |       prev_display() - Deletes a display from the list.
  184. |
  185. |    This allows us to keep a history of where we've been.
  186. |
  187. |    The old display is terminated before the new display is
  188. |    drawn;
  189. */
  190. void 
  191. SwitchDisplay (choice, view_name)
  192.      int choice;
  193.      char *view_name;
  194. {
  195.   DRAWPORT new_dp = NULL;
  196.  
  197.   switch ((INT) choice)
  198.     {
  199.     case PREV_DISPLAY:
  200.       new_dp = prev_display ();
  201.       break;
  202.     case NEXT_DISPLAY:
  203.       new_dp = next_display (view_name);
  204.       break;
  205.     case RESET_DISPLAY:
  206.       new_dp = reset_display (view_name);
  207.       break;
  208.  
  209.     default:
  210.       break;
  211.     }
  212.  
  213.   /* If we can successfully switch displays, cleanup and continue */
  214.   if (new_dp)
  215.     {
  216.       /* Erase the old display */
  217.       (VOID) TdpErase (ActiveDrawport);
  218.       (VOID) TviCloseData (ActiveView);
  219.  
  220.       /* Draw the new display */
  221.       ActiveDrawport = new_dp;
  222.       ActiveView = TdpGetView (ActiveDrawport);
  223.       (VOID) TviOpenData (ActiveView);
  224.       (VOID) TviReadData (ActiveView);
  225.       (VOID) TdpDraw (new_dp);
  226.     }
  227. }
  228.  
  229. /*-----------------------------------------------------------------
  230. |
  231. |  OverlayDisplay
  232. |    Tries to overlay a view to the current display.  The
  233. |    named view is merged to the existing drawing.
  234. |
  235. */
  236. void 
  237. OverlayDisplay (view_name)
  238.      char *view_name;
  239. {
  240.   VIEW overlay_view;
  241.   OBJECT overlay_drawing, overlay_dq, main_dq, test_obj;
  242.   INT i, num_objs;
  243.   DATASOURCELIST main_dsl;
  244.  
  245.   /* Erase the old overlay */
  246.   if (ActiveOverlayName != NULL)
  247.     {
  248.       RemoveDisplay (ActiveOverlayName);
  249.       S_FREE (ActiveOverlayName);
  250.       ActiveOverlayName = NULL;
  251.     }
  252.  
  253.   overlay_view = get_view (view_name);
  254.   if (overlay_view)
  255.     {
  256.       /* Get the overlay drawing and the active view */
  257.       main_dq = VOdrGetObjectDeque (TviGetDrawing (ActiveView));
  258.       overlay_drawing = TviGetDrawing (overlay_view);
  259.       overlay_dq = VOdrGetObjectDeque (overlay_drawing);
  260.  
  261.       /* Test to see if the view has already been overlayed.   test
  262.       |  can be done by seeing if an object that's in the overlay views
  263.       |  is also in the main view.  Another way to manage overlays would
  264.       |  be to have each drawport/screen keep a list of overlayed views.
  265.       */
  266.       test_obj = VOdqGetEntry (overlay_dq, 1);
  267.       if (!VOdqHasEntry (main_dq, test_obj))
  268.         {
  269.  
  270.           /* Merge the overlay into the active view */
  271.           (VOID) TviMergeDrawing (ActiveView, overlay_drawing);
  272.  
  273.           (VOID) TviCloseData (ActiveView);
  274.           main_dsl = TviGetDataSourceList (ActiveView);
  275.           (VOID) TviMergeAddDataSources (overlay_view, main_dsl, DS_EXACTMATCH);
  276.           (VOID) TviOpenData (ActiveView);
  277.           (VOID) TviReadData (ActiveView);
  278.         }
  279.  
  280.       /* Draw the objects in the overlay view */
  281.       num_objs = VOdqSize (overlay_dq);
  282.       for (i = 1; i <= num_objs; i++)
  283.         (VOID) TdpDrawObject (ActiveDrawport, VOdqGetEntry (overlay_dq, i));
  284.  
  285.       /* Set the active overlay name */
  286.       ActiveOverlayName = StrClone (view_name);
  287.     }
  288. }
  289.  
  290. /*-----------------------------------------------------------------
  291. |
  292. |  RemoveDisplay
  293. |    Tries to delete the named view from the current display. The
  294. |    named view is excised from the existing drawing.
  295. |
  296. */
  297. void 
  298. RemoveDisplay (view_name)
  299.      char *view_name;
  300. {
  301.   VIEW overlay_view, main_view;
  302.   OBJECT overlay_drawing;
  303.   OBJECT overlay_dq, main_dq, test_obj;
  304.  
  305.   /* Get the overlay drawing and the active view */
  306.   main_view = TdpGetView (ActiveDrawport);
  307.   main_dq = VOdrGetObjectDeque (TviGetDrawing (main_view));
  308.   overlay_view = get_view (view_name);
  309.   overlay_drawing = TviGetDrawing (overlay_view);
  310.   overlay_dq = VOdrGetObjectDeque (overlay_drawing);
  311.  
  312.   /* Test to see if the view has already been overlayed.   test
  313.     |  can be done by seeing if an object that's in the overlay views
  314.     |  is also in the main view.  Another way to manage overlays would
  315.     |  be to have each drawport/screen keep a list of overlayed views.
  316.     */
  317.   test_obj = VOdqGetEntry (overlay_dq, 1);
  318.  
  319.  
  320.   if (overlay_view && VOdqHasEntry (main_dq, test_obj))
  321.     {
  322.       /* Delete the overlay from the active view */
  323.       (VOID) TviExciseDrawing (main_view, overlay_drawing);
  324.  
  325.       /* We don't need to erase... since in this demo the overlays
  326.       |  always take up the same screen space.
  327.       |  Normally... you'd erase the excised object and repair the
  328.       |  screen below.
  329.       */
  330.     }
  331. }
  332.  
  333. /*-----------------------------------------------------------------
  334. |
  335. |  preload_views
  336. |       The function Pre-Loads the views....of the MAINWINDOW
  337. */
  338. LOCAL void preload_views 
  339. V_P_ ((void))
  340. {
  341.   INT i;
  342.  
  343.   /* For all the room views... load the view, create a drawport, add the
  344.   |  drawport to the symbol table.
  345.   */
  346.   for (i = 0; i < NUM_DISPLAYS; i++)
  347.     (VOID) create_drawport (MainScreen, MainViewNames[i]);
  348.   for (i = 0; i < NUM_OVERLAYS; i++)
  349.     (VOID) load_view (OverlayViewNames[i]);
  350. }
  351.  
  352. /*-----------------------------------------------------------------
  353. |
  354. |  create_drawport
  355. |       The function creates a drawport in the specified area to
  356. |       display the specified view.
  357. |
  358. |       The drawport is added to the symbol tables for easy
  359. |       access by the display manager routines.
  360. |
  361. |       We call RebindData() for all the dynamic objects. This will
  362. |       see if the variable is being controlled by the application and
  363. |       rebind it if necessary.
  364. */
  365. LOCAL DRAWPORT 
  366. create_drawport (screen, viewname)
  367.      OBJECT screen;
  368.      char *viewname;
  369. {
  370.   VIEW view;
  371.   DRAWPORT drawport = NULL;
  372.   RECTANGLE wvp;
  373.  
  374.   /* Load the view */
  375.   view = TviLoad (viewname);
  376.  
  377.   if (view)
  378.     {
  379.       VOobXformBox (TviGetDrawing (view), (OBJECT)0, &wvp );
  380.  
  381.       drawport = TdpCreateStretch (screen, view, DEFAULT_SIZE, &wvp);
  382.  
  383.       /* Add the drawport to the table */
  384.       (VOID) VTstsninsert (DpTable, StrClone (viewname), (INT *) drawport);
  385.  
  386.       /* Open the Data Sources */
  387.       (VOID) TviOpenData (view);
  388.       (VOID) TviReadData (view);
  389.     }
  390.   return drawport;
  391. }
  392.  
  393. /*-----------------------------------------------------------------
  394. |
  395. |  get_drawport
  396. |       Returns the drawport associated with the view_name. If the
  397. |       drawport doesn't exist, it creates one.
  398. */
  399. LOCAL DRAWPORT 
  400. get_drawport (view_name)
  401.      char *view_name;
  402. {
  403.   SYMNODE key;
  404.  
  405.   /* See if the drawport is already in the Drawport Table */
  406.   key = VTstkeyfind (DpTable, view_name);
  407.   if (key)
  408.     return (DRAWPORT) VTsnvalue (key);
  409.   else
  410.     return create_drawport (MainScreen, view_name);
  411. }
  412.  
  413. /*-----------------------------------------------------------------
  414. |
  415. |  load_view
  416. |       Loads the view and adds it to the View Table.  Returns the
  417. |       loaded view.  If the view can't be loaded, (TviLoad returned NULL),
  418. |    return NULL.
  419. */
  420. LOCAL VIEW 
  421. load_view (view_name)
  422.      char *view_name;
  423. {
  424.   VIEW view = NULL;
  425.  
  426.   /* Load the view and add it to the table */
  427.   view = TviLoad (view_name);
  428.   if (view)
  429.     {
  430.       (VOID) VTstsninsert (ViewTable, StrClone (view_name), (INT *) view);
  431.     }
  432.  
  433.   return view;
  434. }
  435.  
  436. /*-----------------------------------------------------------------
  437. |
  438. |  get_view
  439. |       Returns the view associated with the view_name. If the
  440. |       view isn't loaded yet, load it and add it to the ViewTable.
  441. */
  442. LOCAL VIEW 
  443. get_view (view_name)
  444.      char *view_name;
  445. {
  446.   SYMNODE key;
  447.   VIEW view;
  448.  
  449.   /* See if the view is already in the Views Table */
  450.   key = VTstkeyfind (ViewTable, view_name);
  451.   if (key)
  452.     return ((VIEW) VTsnvalue (key));
  453.  
  454.   /* The View must not be loaded yet, so load it */
  455.   view = load_view (view_name);
  456.  
  457.   return view;
  458. }
  459.  
  460. /*-----------------------------------------------------------------
  461. |
  462. |  next_display
  463. |    Tries to add a display to the DisplayList. If the view can't
  464. |    be displayed, returns DV_FAILURE, else DV_SUCCESS.
  465. |
  466. */
  467. LOCAL DRAWPORT 
  468. next_display (view_name)
  469.      char *view_name;
  470. {
  471.   DISPLAY_LIST *prev_display;
  472.   DRAWPORT dp;
  473.  
  474.   /* Get the drawport associated with the view */
  475.   dp = get_drawport (view_name);
  476.   if (dp)
  477.     {
  478.       /* If the list doesn't exist yet, create it.
  479.       |
  480.       |  Make the current list top the previous display. Then,
  481.       |  make the new display the list top. */
  482.       if (!DisplayList)
  483.         {
  484.           prev_display = create_display_list ();
  485.           prev_display->drawport = ActiveDrawport;
  486.         }
  487.       else
  488.         prev_display = DisplayList;
  489.  
  490.       DisplayList = create_display_list ();
  491.       DisplayList->drawport = dp;
  492.       DisplayList->prev = prev_display;
  493.  
  494.     }
  495.  
  496.   return dp;
  497. }
  498.  
  499. /*-----------------------------------------------------------------
  500. |
  501. |  prev_display
  502. |    If their is a previous display, make it the top and
  503. |    deletes the current from the list.
  504. */
  505. LOCAL DRAWPORT prev_display 
  506. V_P_ ((void))
  507. {
  508.   DISPLAY_LIST *prev_display;
  509.  
  510.   /* Make sure we have a list and a previous display before
  511.   |  you make the previous display the top.
  512.   */
  513.   if (DisplayList && DisplayList->prev)
  514.     {
  515.       prev_display = DisplayList->prev;
  516.       del_display_list_item (DisplayList);
  517.       DisplayList = prev_display;
  518.       return DisplayList->drawport;
  519.     }
  520.   else
  521.     return (DRAWPORT) NULL;
  522. }
  523.  
  524. /*-----------------------------------------------------------------
  525. |
  526. |  reset_display
  527. |    Resets the Display List, deletes the previous list and makes
  528. |    the TOP_VIEW the beginning of the list.
  529. */
  530. LOCAL DRAWPORT 
  531. reset_display (view_name)
  532.      char *view_name;
  533. {
  534.  
  535.   /* Destroy the DisplayList */
  536.   destroy_display_list (&DisplayList);
  537.  
  538.   /* return the Active Drawport specified View */
  539.   return get_drawport (view_name);
  540.  
  541. }
  542.  
  543. /*-----------------------------------------------------------------
  544. |
  545. |  create_display_list - Allocates and initializes a DISPLAY_LIST.
  546. */
  547. LOCAL DISPLAY_LIST *create_display_list 
  548. V_P_ ((void))
  549. {
  550.   DISPLAY_LIST *display_info;
  551.  
  552.   display_info = (DISPLAY_LIST *) S_ALLOC ((LONG) sizeof (DISPLAY_LIST));
  553.   display_info->drawport = (DRAWPORT) NULL;
  554.   display_info->prev = (DISPLAY_LIST *) NULL;
  555.   return display_info;
  556. }
  557.  
  558. /*-----------------------------------------------------------------
  559. |
  560. |  del_display_list_item - Frees a DISPLAY_LIST item.
  561. */
  562. LOCAL void 
  563. del_display_list_item (display_item)
  564.      DISPLAY_LIST *display_item;
  565. {
  566.   S_FREE ((CHAR *) display_item);
  567. }
  568.  
  569. /*-----------------------------------------------------------------
  570. |
  571. |  destroy_display_list - Destroys the DISPLAY_LIST by freeing each item.
  572. */
  573. LOCAL void 
  574. destroy_display_list (display_info)
  575.      DISPLAY_LIST **display_info;
  576. {
  577.   DISPLAY_LIST *info, *prev;
  578.  
  579.   info = *display_info;
  580.   while (info)
  581.     {
  582.       prev = info->prev;
  583.       del_display_list_item (info);
  584.       info = prev;
  585.     }
  586.   *display_info = NULL;
  587. }
  588.  
  589. /*-----------------------------------------------------------------
  590. |
  591. |  TermDisplays
  592. |       Performs the termination and clean up needed for the display
  593. |    components. This balances InitDisplays().
  594. |
  595. |    The Drawport and View Tables are "traversed" to clean up
  596. |    the views and drawports.  We also clean up the table as we go.
  597. |    The AreaCoords used to define drawport areas are "freed". The
  598. |    display manager's DisplayList is destroyed. Finally, the screen
  599. |    is erased and closed.
  600. */
  601. void TermDisplays 
  602. V_P_ ((void))
  603. {
  604.   CHAR *viewname;
  605.   SYMNODE node;
  606.   VIEW view;
  607.   DRAWPORT dp;
  608.  
  609.   /* Destroy the display drawports */
  610.   while (VTstlen (DpTable) > 0)
  611.     {
  612.       node = VTstsnget (DpTable, 0);
  613.       viewname = VTsnkey (node);
  614.       S_FREE (viewname);
  615.       dp = (DRAWPORT) VTsnvalue (node);
  616.       view = TdpGetView (dp);
  617.       (VOID) TdpDestroy (dp);
  618.       (VOID) TviDestroy (view);
  619.       VTstsnremove (DpTable, node);
  620.     }
  621.   VTstdestroy (DpTable);
  622.  
  623.   /* Destroy the overlay views */
  624.   while (VTstlen (ViewTable) > 0)
  625.     {
  626.       node = VTstsnget (ViewTable, 0);
  627.       viewname = VTsnkey (node);
  628.       S_FREE (viewname);
  629.       (VOID) TviDestroy ((VIEW) VTsnvalue (node));
  630.       VTstsnremove (ViewTable, node);
  631.     }
  632.   VTstdestroy (ViewTable);
  633.  
  634.   /* Destroy the Display List */
  635.   destroy_display_list (&DisplayList);
  636.  
  637.   /* Destroy, Erase and Close the Main Window */
  638.   (VOID) TscClose (MainScreen);
  639.  
  640.   /* Destroy the Menu Window */
  641.   (VOID) TscClose (MenuScreen);
  642. }
  643.  
  644. /*==================================================================
  645. |
  646. |    StrClone
  647. |       Allocates space for and makes a copy of a specified string,
  648. |       returning a pointer to the string.  If there is no input
  649. |       string, the routine returns a NULL.
  650. */
  651. CHAR *StrClone( str )
  652.   CHAR *str;
  653.   {
  654.   FAST INT len;
  655.   CHAR *newstr;
  656.   FAST CHAR *to;
  657.   FAST CHAR *from;
  658.  
  659.   if( str == NULL )
  660.     return( NULL );
  661.   len = S_STRLEN( str ) + 1;
  662.   newstr = (CHAR *)S_ALLOC( len );
  663.   if( newstr )
  664.     for( to = newstr, from = str; len > 0; len-- )
  665.       *to++ = *from++;
  666.   return( newstr );
  667.   }
  668.  
  669.  
  670.